#include "rtda/heap/cp_fieldref.h"
#include "rtda/heap/class.h"
#include "rtda/heap/field.h"
#include <stdexcept>

namespace jvm {
namespace heap {

FieldRef::FieldRef(std::shared_ptr<ConstantPool> pCp, const std::shared_ptr<classfile::ConstantMemberRefInfo> pMemberrefInfo) 
    : MemberRef(pCp) {
    copyMemberRefInfo(pMemberrefInfo);
}

std::shared_ptr<FieldRef> FieldRef::newFieldRef(std::shared_ptr<ConstantPool> pCp, const std::shared_ptr<classfile::ConstantFieldRefInfo> pFieldrefInfo) {
    return std::make_shared<FieldRef>(pCp, pFieldrefInfo);
}

std::shared_ptr<Field> FieldRef::resolvedField() {
    if (!m_pField) {
        resolveFieldRef();
    }
    return m_pField;
}

void FieldRef::resolveFieldRef() {
    std::shared_ptr<Class> pD = m_pCp->getClass();
    std::shared_ptr<Class> pC = resolvedClass();
    m_pField = lookupField(pC, m_name, m_descriptor);

    if (!m_pField) {
        throw std::runtime_error("java.lang.NoSuchFieldError");
    }
    if (!m_pField->isAccessibleTo(pD)) {
        throw std::runtime_error("java.lang.IllegalAccessError");
    }
}

std::shared_ptr<Field> FieldRef::lookupField(std::shared_ptr<Class> pC, const std::string& name, const std::string& descriptor) {
    for (std::shared_ptr<Field> pField : pC->getFields()) {
        if (pField->getName() == name && pField->getDescriptor() == descriptor) {
            return pField;
        }
    }

    for (std::shared_ptr<Class> pIface : pC->getInterfaces()) {
        if (std::shared_ptr<Field> pField = lookupField(pIface, name, descriptor)) {
            return pField;
        }
    }

    if (std::shared_ptr<Class> pSuperClass = pC->getSuperClass()) {
        return lookupField(pSuperClass, name, descriptor);
    }

    return nullptr;
}

} // namespace heap
} // namespace jvm